﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Drawing;

namespace GreenArrow
{
    class CompareBitmap
    {
        Bitmap compareBmp;
        Marker marker;
        public static int OneDepth = 30;
        public static int Depth = 100;
        List<OffsetPoint> offsetPoints = new List<OffsetPoint>();
        public CompareBitmap(Bitmap _bmp) {
            compareBmp = _bmp;
            GenerateOffsetPoints();
        }
        public CompareBitmap(Marker _marker)
        {
            marker = _marker;
            GenerateOffsetPointsByMarker();
        }
        void GenerateOffsetPointsByMarker() {
            List<OffsetPoint>[] ops = new List<OffsetPoint>[marker.pieces.Count * 4];
            int cpIndex = 0;
            int maxDepth = 0;
            foreach (ConditionPiece cp in marker.pieces) {

                for (int c = 0; c < 4; c++) {
                    int index = cpIndex * 4 + c;
                    ops[index] = new List<OffsetPoint>();
                    int x = 0;
                    int y = 0;
                    if (c == 0) {
                        x = 0;
                        y = 0;
                    }
                    else if (c == 1) {
                        x = cp.Width - 1;
                        y = 0;
                    }
                    else if (c == 2)
                    {
                        x = cp.Width - 1;
                        y = cp.Height - 1;
                    }
                    else if (c == 3)
                    {
                        x = 0;
                        y = cp.Height - 1;
                    }
                    Color lastColor = Color.White;
                    while (ops[index].Count < OneDepth) {
                        Color pixel = cp.bitmap.GetPixel(x, y);
                        if (pixel.Equals(lastColor))
                        {
                            goto NEXTROUND;
                        }
                        ops[index].Add(new OffsetPoint(x + cp.RelativeX, y + cp.RelativeY, pixel));
                        if (ops[index].Count > maxDepth) {
                            maxDepth = ops[index].Count;
                        }
                        lastColor = pixel;
                    NEXTROUND:
                        if (c == 0) {
                            x++;
                            if (x >= cp.Width) {
                                x = 0;
                                y++;
                            }
                            if (y >= cp.Height) {
                                break;
                            }
                        }
                        else if (c == 1)
                        {
                            y++;
                            if (y >= cp.Height)
                            {
                                y = 0;
                                x--;
                            }
                            if (x < 0)
                            {
                                break;
                            }
                        }
                        else if (c == 2)
                        {
                            x--;
                            if (x < 0)
                            {
                                x = cp.Width - 1;
                                y--;
                            }
                            if (y < 0)
                            {
                                break;
                            }
                        }
                        else if (c == 3)
                        {
                            y--;
                            if (y < 0)
                            {
                                y = cp.Height - 1;
                                x++;
                            }
                            if (x >= cp.Width)
                            {
                                break;
                            }
                        }
                    }
                }
                cpIndex++;
            }
            Hashtable ht = new Hashtable();
            int idx = 0;
            int ci = 0;
            while (offsetPoints.Count < Depth && idx < maxDepth)
            {
                if (idx < ops[ci].Count)
                {
                    OffsetPoint op = ops[ci][idx];
                    if (!ht.ContainsKey(op.X * 10000 + op.Y))
                    {
                        ht[op.X * 10000 + op.Y] = 1;
                        offsetPoints.Add(op);
                    }
                }
                ci++;
                if (ci == marker.pieces.Count * 4)
                {
                    ci = 0;
                    idx++;
                }
            }
        }
        void GenerateOffsetPoints() {
            int Width = compareBmp.Width;
            int Height = compareBmp.Height;
            
            List<OffsetPoint>[] ops = new List<OffsetPoint>[4];
            for (int c = 0; c < 4; c++) {
                ops[c] = new List<OffsetPoint>();
            }
            Color lastColor;
            lastColor = Color.White;
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    Color pixel = compareBmp.GetPixel(x, y);
                    if (pixel.Equals(lastColor)) {
                        continue;
                    }
                    ops[0].Add(new OffsetPoint(x, y, pixel));
                    lastColor = pixel;
                    if (ops[0].Count >= OneDepth)
                    {
                        goto FIRST_END;
                    }
                }
            }
            FIRST_END:
            lastColor = Color.White;
            for (int x = Width - 1; x >= 0; x--)
            {
                for (int y = 0; y < Height; y++)
                {
                    Color pixel = compareBmp.GetPixel(x, y);
                    if (pixel.Equals(lastColor)) {
                        continue;
                    }
                    ops[1].Add(new OffsetPoint(x, y, pixel));
                    lastColor = pixel;
                    if (ops[1].Count >= OneDepth)
                    {
                        goto SECOND_END;
                    }
                }
            }
            SECOND_END:
            lastColor = Color.White;
            for (int y = Height - 1; y >= 0; y--)
            {
                for (int x = Width - 1; x >= 0; x--)
                {
                    Color pixel = compareBmp.GetPixel(x, y);
                    if (pixel.Equals(lastColor)) {
                        continue;
                    }
                    ops[2].Add(new OffsetPoint(x, y, pixel));
                    lastColor = pixel;
                    if (ops[2].Count >= OneDepth)
                    {
                        goto THIRD_END;
                    }
                }
            }
            THIRD_END:
            lastColor = Color.White;
            for (int x = 0; x < Width; x++)
            {
                for (int y = Height - 1; y >= 0; y--)
                {
                    Color pixel = compareBmp.GetPixel(x, y);
                    if (pixel.Equals(lastColor)) {
                        continue;
                    }
                    ops[3].Add(new OffsetPoint(x, y, pixel));
                    lastColor = pixel;
                    if (ops[3].Count >= OneDepth)
                    {
                        goto FORTH_END;
                    }
                }
            }
        FORTH_END:
            int maxDepth = Math.Max(Math.Max(ops[0].Count, ops[1].Count), Math.Max(ops[2].Count, ops[3].Count));
        Hashtable ht = new Hashtable();
            int index = 0;
            int ci = 0;
            while (offsetPoints.Count < Depth && index < maxDepth)
            {
                if (index < ops[ci].Count) {
                    OffsetPoint op = ops[ci][index];
                    if (! ht.ContainsKey(op.X * 10000 + op.Y))
                    {
                        ht[op.X * 10000 + op.Y] = 1;
                        offsetPoints.Add(op);
                    }
                }
                ci++;
                if (ci == 4)
                {
                    ci = 0;
                    index++;
                }
            }
        }
        public bool Compare(Bitmap targetBmp, int offsetX, int offsetY) {
            int maxMatch = 0;
            foreach (OffsetPoint offsetPoint in offsetPoints) { 
                Color expectedColor = offsetPoint.Color;
                int absoluteX = offsetPoint.X + offsetX;
                int absoluteY = offsetPoint.Y + offsetY;
                Color color = targetBmp.GetPixel(absoluteX, absoluteY);
                if (!color.Equals(expectedColor)) {
                    /*
                    if (maxMatch > 5)
                    {
                        System.Console.Write(maxMatch + ",");
                    }
                    */
                    return false;
                }
                maxMatch++;
            }
            return true;
        }
    }
}
